package com.xlf.service.base.service.importProblem.impl;

import cn.hutool.core.io.file.FileWriter;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.XmlUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xlf.common.core.exception.BizException;
import com.xlf.common.core.util.file.UploadConfig;
import com.xlf.pojo.base.Language;
import com.xlf.pojo.base.Problem;
import com.xlf.pojo.base.ProblemCase;
import com.xlf.pojo.base.ProblemLanguage;
import com.xlf.pojo.constant.Constants;
import com.xlf.service.base.mapper.*;
import com.xlf.service.base.pojo.vo.ImportProblemVO;
import com.xlf.service.base.service.ProblemService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

import javax.annotation.Resource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author xlf 2734029211@qq.com
 * @Date 2023/12/5 11:17
 * @Description: TODO 解析fps文件生成problem
 * @Version 1.0
 */
@Service
@Slf4j
public class FpsProblemImportService extends ProblemImportService{


    private final static List<String> timeUnits = Arrays.asList("ms", "s");
    private final static List<String> memoryUnits = Arrays.asList("kb", "mb");
    private static final Map<String, String> fpsMapHOJ = new HashMap<String, String>() {
        {
            put("Python", "Python");
            put("Go", "Go");
            put("C", "C");
            put("C++", "C++");
            put("Java", "Java");
            put("C#", "C#");
        }
    };

    @Transactional
    public void importProblem(String workFileDir) {

        File file = new File(workFileDir);
        File[] files = file.listFiles();

        //获取xml文件
        HashMap<String, File> xmlFiles = getFileBySuffix(file.listFiles(), "xml");

        //获取JSON题目文件
        HashMap<String, File> problemInfo = getJSONFile(files);

        //读取json文件生成对象
        HashMap<String, ImportProblemVO> problemVoMap = getImportProblemVOHashMap(problemInfo);

        //获取系统语言
        Map<String, Language> languageMap = getLanguageMap();

        //获取系统标签
        Map<String, Long> tagMap = getTagMap();

        //默认模板
        Problem defaultProblem = getProblem(languageMap, tagMap,problemVoMap, "default");

        //覆盖模板
        Problem coverProblem = getProblem(languageMap, tagMap,problemVoMap, "cover");

        ArrayList<Problem> problems = new ArrayList<>();

        //解析所有fps文件
        xmlFiles.forEach((fileName,fileItem) -> {

            FileInputStream fileInputStream = null;
            try {
                fileInputStream = new FileInputStream(fileItem);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                throw new BizException("文件名为[" + fileName + "]的文件找不到！");
            }
            Document document = getDocument(fileInputStream);

            Element rootElement = XmlUtil.getRootElement(document);

            String version = rootElement.getAttribute("version");

            for (Element item : XmlUtil.getElements(rootElement, "item")) {

                Problem problem = new Problem();

                Element title = XmlUtil.getElement(item, "title");

                Element descriptionElement = XmlUtil.getElement(item, "description");

                Element inputElement = XmlUtil.getElement(item, "input");

                Element outputElement = XmlUtil.getElement(item, "output");

                Element hintElement = XmlUtil.getElement(item, "hint");

                Element sourceElement = XmlUtil.getElement(item, "source");
                // ms
                Integer timeLimit = getTimeLimit(version, item);
                // mb
                Integer memoryLimit = getMemoryLimit(version, item);

                // 题面用例
                List<Element> sampleInputs = XmlUtil.getElements(item, "sample_input");
                List<Element> sampleOutputs = XmlUtil.getElements(item, "sample_output");
                StringBuilder examples = new StringBuilder();
                for (int i = 0; i < sampleInputs.size(); i++) {
                    examples.append("<input>").append(sampleInputs.get(i).getTextContent()).append("</input>");
                    examples.append("<output>").append(sampleOutputs.get(i).getTextContent()).append("</output>");
                }

                // 语言模板
                Map<String, String> codeTemplateMap = new HashMap<>();
                List<Element> templateNodes = XmlUtil.getElements(item, "template");
                for (Element templateNode : templateNodes) {
                    String templateLanguage = templateNode.getAttribute("language");
                    String templateCode = templateNode.getTextContent();
                    if (templateLanguage != null && templateCode != null) {
                        String lang = fpsMapHOJ.get(templateLanguage);
                        if (lang != null) {
                            codeTemplateMap.put(lang,templateCode);
                        }
                    }
                }
                List<ProblemLanguage> languages = new ArrayList<>();
                codeTemplateMap.forEach((lang,code) -> {
                    Language language = languageMap.getOrDefault(lang, null);
                    if (language == null) {
                        throw new BizException("请检查标题为：" + title + "的题目的代码语言是否有错，不要添加不支持的语言！");
                    }
                    languages.add(new ProblemLanguage()
                            .setLid(language.getId())
                            .setCode(code)
                            .setName(lang));
                });


                // 题目评测数据
                List<Element> testInputs = XmlUtil.getElements(item, "test_input");
                List<Element> testOutputs = XmlUtil.getElements(item, "test_output");

                boolean isNotOutputTestCase = CollectionUtils.isEmpty(testOutputs);

                String testCaseFileDir = initFileDir();

                for (int i = 0; i < testInputs.size(); i++) {
                    String infileName = (i + 1) + ".in";
                    String outfileName = (i + 1) + ".out";
                    FileWriter infileWriter = new FileWriter(testCaseFileDir + File.separator + infileName);
                    FileWriter outfileWriter = new FileWriter(testCaseFileDir + File.separator + outfileName);
                    infileWriter.write(testInputs.get(i).getTextContent());
                    outfileWriter.write(isNotOutputTestCase ? "" : testOutputs.get(i).getTextContent());
                }

                ArrayList<ProblemCase> problemCases = getProblemCases(testCaseFileDir);


                problem.setTitle(title.getTextContent());
                problem.setDescription(descriptionElement.getTextContent());
                problem.setInput(inputElement.getTextContent());
                problem.setOutput(outputElement.getTextContent());
                problem.setPrompt(hintElement.getTextContent());
                problem.setSource(sourceElement.getTextContent());
                problem.setTimeLimit(timeLimit);
                problem.setMemoryLimit(memoryLimit * 1024);
                problem.setStackLimit(memoryLimit * 1024);
                problem.setExamples(examples.toString());
                problem.setProblemCase(problemCases);
                problem.setProblemLanguageList(languages);
                problem.setAuth(1);
                problem.setCodeShare(1);

                problems.add(problem);

            }

        });

        problems.forEach(item -> {
            importProblemBeforeHandle(defaultProblem,coverProblem,item);
            problemService.saveOrUpdate_(item);
        });



    }

    private Document getDocument(InputStream inputStream) {
        Document document = null;

        try {

            DocumentBuilderFactory documentBuilderFactory = XmlUtil.createDocumentBuilderFactory();
            documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false);
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            document = documentBuilder.parse(inputStream);

        } catch (ParserConfigurationException e) {
            log.error("build  DocumentBuilder error:", e);
        } catch (IOException e) {
            log.error("read xml file error:", e);
        } catch (SAXException e) {
            log.error("parse xml file error:", e);
        }
        if (document == null) {
            throw new BizException("读取xml失败，请检查FPS文件格式是否准确！");
        }
        return document;
    }

    private Integer getTimeLimit(String version, Element item) {
        Element timeLimitNode = XmlUtil.getElement(item, "time_limit");
        String timeUnit = timeLimitNode.getAttribute("unit");
        String timeLimit = timeLimitNode.getTextContent();
        int index = timeUnits.indexOf(timeUnit.toLowerCase());
        if ("1.1".equals(version)) {
            if (index == -1) {
                index = 1;
            }
            return Integer.parseInt(timeLimit) * (int) Math.pow(1000, index);
        } else {
            if (index == -1) {
                throw new RuntimeException("Invalid time limit unit:" + timeUnit);
            }
            double tmp = (Double.parseDouble(timeLimit) * Math.pow(1000, index));
            return (int) tmp;
        }
    }

    private Integer getMemoryLimit(String version, Element item) {
        Element memoryLimitNode = XmlUtil.getElement(item, "memory_limit");
        String memoryUnit = memoryLimitNode.getAttribute("unit");
        String memoryLimit = memoryLimitNode.getTextContent();
        int index;
        index = memoryUnits.indexOf(memoryUnit.toLowerCase());
        if ("1.1".equals(version)) {
            index = 1;
        }
        if (index == -1) {
            throw new RuntimeException("Invalid memory limit unit:" + memoryUnit);
        }
        return Integer.parseInt(memoryLimit) * (int) Math.pow(1000, index - 1);
    }


}
